﻿using System;
using System.Collections.Generic;
using System.Linq;

using SharedGenomics.Core;
using SharedGenomics.Annotations;

namespace SharedGenomics.Workbench.Annotations
{
    /// <summary>
    /// There is not always a one-to-one relationship between 'logical' annotation requests (i.e. a request for a single annotation) and 'physical' requests, 
    /// which generally correspond to calls to external services for data. For example Taverna outputs will often correspond to multiple annotations displayed to 
    /// the user. We want to keep such details hidden from the rest of the system, such as the <see cref="IAnnotationPlan"/> which should have knowledge only of 
    /// physical annotations rather than the mechanism for obtaining them. This stage is responsible for mapping multiple logical requests into a single physical 
    /// request - there must also be a corresponding stage for the reverse mapping (i.e. creating multiple logical annotations from a single data item). This 
    /// reverse mapping is implemented in the <see cref="DecomposeAnnotationRequestStage"/>
    /// </summary>
    public class AggregateAnnotationRequestStage : IAnnotationRequestStage
    {
        //The ncbi and taverna 'targets' sets track the annotation targets for which a taverna/ncbi request has been issued. If a request is made for an annotation
        //obtained from these requests again, it will not be issued since either the annnotation is available or it is in progress
        private HashSet<object> _tavernaTargets;

        private object _tavernaLock = new object();

        public AggregateAnnotationRequestStage()
        {
            this._tavernaTargets = new HashSet<object>();
        }

        public void Process(AnnotationRequestContext context, IAnnotationRequestPipelineTraversalStrategy traversalStrategy)
        {
            bool isTavernaRequest = context.RequestKey.In(AnnotationKey.Gene, AnnotationKey.KeggPathway, AnnotationKey.Publications, AnnotationKey.KeggId);
            bool annotationExists = context.ContainsResultFor(context.RequestKey);

            if (!annotationExists && isTavernaRequest)
            {
                //see if a taverna request has already been issued for the current target
                //if it hasn't then add update the context annotation key to Taverna and note that a request has been issued
                //if not then halt the pipeline since a request is in progress
                bool shouldIssue = false;
                lock (this._tavernaLock)
                {
                    shouldIssue = !this._tavernaTargets.Contains(context.AnnotationTarget);
                    if (shouldIssue)
                    {
                        this._tavernaTargets.Add(context.AnnotationTarget);
                    }
                }

                if (shouldIssue)
                {
                    context.RequestKey = AnnotationKey.Taverna;
                    traversalStrategy.MoveNext(context);
                }
            }
            else
            {
                //nothing to do for this request
                traversalStrategy.MoveNext(context);
            }
        }

        private bool IssueTarget(object target, HashSet<object> issuedTargets, object targetLock)
        {
            lock (targetLock)
            {
                bool issued = issuedTargets.Contains(target);
                if (!issued)
                {
                    issuedTargets.Add(target);
                }

                return issued;
            }
        }
    }
}
